class Observer {
  constructor(data){
    this.walk(data)
  }
  // 处理 vm实例中的 data数据，给数据内的属性添加getter和setter
  walk(data){
    if(!data || typeof data !== 'object') return

    Object.keys(data).forEach(key =>{
      this.defineReactive(data,key,data[key])
    })
  }

  // 给数据内的属性添加getter和setter
  defineReactive(obj,key,val){
    let that = this
     // 对象嵌套递归
    this.walk(val)
    
    // 收集依赖，发送通知
    let dep = new Dep()

    // 数据劫持
    Object.defineProperty(obj,key,{
      enumerable:true,
      configurable:true,
      get(){
        // 收集依赖
        Dep.target && dep.addSubs(Dep.target)
        // 注意这里不能 obj[key] 为什么？
        // 访问 vm.msg时，会触发vuejs中代理，指向vm.data.msg
        // 而data.msg 又会触发这里的get，如果这个get又返回了obj[key] (即data.msg)，则死递归了
        // val是局部变量，局部变量在方法结束会被释放。外面vm.msg为什么还能访问到val？
        // 因为这里的obj是对象，外部对其有引用，变量没有被移除，产生了闭包，所有可以访问
        return val 
      },
      set(newVal){        
        if(newVal === val) return 
        val = newVal
        
        // 当data内的数据重新赋值新对象类型时（vm.msg={}）
        // 需要给新对象添加数据劫持（添加getter和setter）
        // 此处this指data，而不是Observer2
        that.walk(newVal) 
        
        // 发送通知
        dep.notify()
      }
    })
  }
}